package ar.com.jsejtman.common.service.data;

import java.util.List;

/**
 * Define una página de datos estándar. Es necesaria para trabajar búsquedas de información con
 * paginación.
 * 
 * @author jsejtman
 * 
 * @param <E> Entidad de negocio a la que corresponde esta página de datos.
 */
public class DataPage<E extends Object> {

	/**
	 * Tamaño de la página de datos actual.
	 */
	private Integer limit;

	/**
	 * Número actual de la página de datos actual.
	 */
	private Integer page;

	/**
	 * Cantidad total de datos (NO de páginas).
	 */
	private Integer records;

	/**
	 * Total de páginas.
	 */
	private Integer total;

	/**
	 * Datos correspondiente a la página actual.
	 */
	private List<E> rows;

	/**
	 * Obtiene el tamaño de la página.
	 * 
	 * @return Tamaño de la página.
	 */
	public final Integer getLimit() {

		return (this.limit);
	}

	/**
	 * Asigna el tamaño de la página.
	 * 
	 * @param pageSize Tamaño de la página.
	 */
	public final void setLimit(final Integer pageSize) {

		this.limit = pageSize;
	}

	/**
	 * Obtiene el número actual de la página.
	 * 
	 * @return Número actual de la página.
	 */
	public final Integer getPage() {

		return (this.page);
	}

	/**
	 * Asigna el número actual de la página.
	 * 
	 * @param currentPageNumber Número actual de la página.
	 */
	public final void setPage(final Integer currentPageNumber) {

		this.page = currentPageNumber;
	}

	/**
	 * Obtiene la cantidad total de datos.
	 * 
	 * @return Cantidad total de datos.
	 */
	public final Integer getRecords() {

		return (this.records);
	}

	/**
	 * Asigna la cantidad total de datos.
	 * 
	 * @param totalResults Cantidad total de datos.
	 */
	public final void setRecords(final Integer totalResults) {

		this.records = totalResults;
	}

	/**
	 * Obtiene la página actual de datos.
	 * 
	 * @return página actual de datos.
	 */
	public final List<E> getRows() {

		return (this.rows);
	}

	/**
	 * Asigna la página actual de datos.
	 * 
	 * @param data página actual de datos.
	 */
	public final void setRows(final List<E> data) {

		this.rows = data;
	}

	/**
	 * el total.
	 * 
	 * @return the total
	 */
	public final Integer getTotal() {

		return this.total;
	}

	/**
	 * Asigna un nuevo valor a total.
	 * 
	 * @param total El valor a asignar a total.
	 */
	public final void setTotal(final Integer total) {

		this.total = total;
	}

	/**
	 * Verifica si es la primera página del total de datos disponibles.
	 * 
	 * @return True si lo es; caso contrario, False.
	 */
	public final boolean isFirstPage() {

		if (this.rows == null || this.rows.isEmpty()) {
			return (true);
		}

		return (this.page == 1);
	}

	/**
	 * Verifica si es la última página del total de datos disponibles.
	 * 
	 * @return True si lo es; caso contrario, False.
	 */
	public final boolean isLastPage() {

		if (this.rows == null || this.rows.isEmpty()) {
			return (true);
		}

		return (this.getLastPageNumber() == this.page);
	}

	/**
	 * Obtiene el número de posición al que corresponde el primer elemento de la página, evaluado
	 * dentro del total de elementos disponibles.
	 * 
	 * @return Número de posición del primer elemento de la página.
	 */
	public final Integer getStartingOffset() {

		if (this.rows == null || this.rows.isEmpty()) {
			return (0);
		}

		if (this.hasInvalidCurrentPageNumber() || this.hasInvalidPageSize()) {
			return (0);
		}

		return (((this.page - 1) * this.limit) + 1);
	}

	/**
	 * Obtiene el número de posición al que corresponde el último elemento de la página, evaluado
	 * dentro del total de elementos disponibles.
	 * 
	 * @return Número de posición del último elemento de la página.
	 */
	public final Integer getEndingOffset() {

		if (this.rows == null || this.rows.isEmpty()) {
			return (0);
		}

		if (this.hasInvalidCurrentPageNumber() || this.hasInvalidPageSize()) {
			return (0);
		}

		return (this.page * this.limit);
	}

	/**
	 * Obtiene el número de la última página.
	 * 
	 * @return Número de la última página.
	 */
	public final Integer getLastPageNumber() {

		if (this.hasInvalidTotalResults() || this.hasInvalidPageSize()) {
			return (0);
		}

		return (this.records / this.limit + ((this.records % this.limit > 0) ? 1 : 0));
	}

	/**
	 * Indica si el número de página actual es inválido.
	 * 
	 * @return True si el número de página actual es inválido; caso contrario, False.
	 */
	private boolean hasInvalidCurrentPageNumber() {

		return (this.page == null || this.page <= 1);
	}

	/**
	 * Indica si el tamaño de página es inválido.
	 * 
	 * @return True si el tamaño de página es inválido; caso contrario, False.
	 */
	private boolean hasInvalidPageSize() {

		return (this.limit == null || this.limit <= 1);
	}

	/**
	 * Indica si la cantidad total de resultados es inválida.
	 * 
	 * @return True si la cantidad total de resultados es inválida; caso contrario, False.
	 */
	private boolean hasInvalidTotalResults() {

		return (this.records == null || this.records <= 1);
	}

}
